#!/usr/bin/env python3
#
# Location editor for weathercomp.

import cmd
import json
import sqlite3
import urllib.request

class LocationShell(cmd.Cmd):
    intro = 'Location editor for Weathercomp.\n\
Type help or ? to list commands.\n'
    prompt = 'loc> '
    con = None

    def do_list(self, arg):
        """List the locations currently stored in the database."""
        cur = self.con.cursor()
        query = ''
        try:
            locid = int(arg)
            query = 'SELECT LocID, Name, Latitude, Longitude \
                     FROM Location WHERE LocID={0};'.format(locid)
        except ValueError:
            query = 'SELECT LocID, Name, Latitude, Longitude FROM Location;'
        print('ID | NAME                    | LAT  | LON   ')
        print('--------------------------------------------')
        printrow = '{LocID:3d}|{Name:25}|{Latitude:6.2f}|{Longitude:7.2f}'
        for row in cur.execute(query):
            print(printrow.format(**row))

    def do_remove(self, arg):
        """Remove a location from the list based on the ID.""" 
        locid = None
        try:
            locid = int(arg)
        except ValueError:
            pass
        if locid == None:
            print('Invalid or missing argument.')
        else:
            print('Deleting item {0}...'.format(locid))
            query = 'DELETE FROM Location WHERE LocID=?;'
            with self.con as con:
                cur = con.execute(query, (locid,))
                if cur.rowcount == 1:
                    print('Location deleted from database.')
                else:
                    print('Nothing deleted.')

    def do_add(self, arg):
        """Add a location. 
        Argument: search string, which can be a city name or 4-letter ICAO 
        airport code.
        """
        apiquery = 'http://autocomplete.wunderground.com/\
aq?query={0}&format=JSON'.format(arg)
        with urllib.request.urlopen(apiquery) as f:
            # the RESULTS key is guaranteed to be there as long as the request
            # is well-formed.
            try:
                cities_raw = json.loads(f.read().decode('utf-8'))['RESULTS']
            except KeyError:
                print('Invalid query or broken server.')
                return

        cities = []
        for city in cities_raw:
            # zmw is the unique location ID in WU's DB
            cities.append((city['name'], city['zmw']))
        if len(cities) == 0:
            print('No matches for {0}. Try a different spelling,\
or accept that not every database is perfect.'.format(arg))
            return

        print('Please choose one of the matches.')
        chosencity = self.choose_item(cities)
        apikey = open('apikeys/wunderground.key').read().strip()
        apiquery = 'http://api.wunderground.com/\
api/{0}/geolookup/q/zmw:{1}.json'.format(apikey, chosencity[1])
        with urllib.request.urlopen(apiquery) as f:
            location = json.loads(f.read().decode('utf-8'))['location']
        lat = location['lat']
        lon = location['lon']
        name = chosencity[0]

        query = 'INSERT INTO Location (Name, Latitude, Longitude) \
                 VALUES (?, ?, ?)'
        with self.con as con:
            cur = con.execute(query, (name, lat, lon))
            if cur.rowcount == 1:
                self.onecmd('list {0}'.format(cur.lastrowid))
            else:
                # Don't think this can even happen without some exception
                # handling earlier on, but meh.
                print('Failed to add new location')

    def do_exit(self, arg):
        self.con.commit()
        self.con.close()
        print()
        return True

    def do_EOF(self, arg):
        return self.do_exit(arg)

    def preloop(self):
        self.con = sqlite3.connect('weathercomp.db')
        self.con.row_factory = sqlite3.Row

    def choose_item(self, items):
        """Present a multiple-choice thing to the user.

        Given a list of (name, value) tuples this will display a numbered list
        of the names.  The user enters a number, and the associated tuple is
        returned. 
        """
        i = 0
        choice = 0
        for k, v in items:
            print('\t[{0}] {1}'.format(i, k))
            i += 1
        try:
            choice = int(input('Enter the number (default 0): '))
        except ValueError:
            pass
        if choice < 0 or choice >= len(items):
            choice = 0
        return items[choice]

if __name__ == '__main__':
    LocationShell().cmdloop()